Skip to content

Conversation

@wolf31o2
Copy link
Member

@wolf31o2 wolf31o2 commented Nov 7, 2025

Closes #187

This PR adds the EpochNumber field to ChainSyncStatus and restores license headers.

Signed-off-by: Chris Gianelloni [email protected]

Summary by CodeRabbit

  • New Features

    • Chain sync now reports the current epoch number in status updates.
    • Configuration support added for Byron and Shelley genesis parameters with sensible defaults.
  • Bug Fixes

    • Improved epoch derivation and handling around era boundaries.
    • Simplified transaction lookup format and more reliable rollback behavior resetting epoch info.
  • Tests

    • Tests updated to cover epoch tracking, rollback, and related adjustments.
  • Documentation

    • README expanded with environment variable and genesis configuration examples.

@wolf31o2 wolf31o2 requested a review from a team as a code owner November 7, 2025 04:16
@coderabbitai
Copy link

coderabbitai bot commented Nov 7, 2025

📝 Walkthrough

Walkthrough

Adds epoch computation and exposure in chain sync: new EpochFromSlot(slot uint64) uint64 computes an epoch from a slot using Byron and Shelley genesis parameters (with guards for zero lengths). ChainSyncStatus gains EpochNumber uint64 which is populated on status updates and rollbacks. Transaction input resolution was simplified to use a txIndex@txId identifier and query kugo.Transaction(txID). Configuration gained ByronGenesisConfig and ShelleyGenesisConfig types plus ByronGenesis and ShelleyGenesis fields on Config with populateByronGenesis()/populateShelleyGenesis()/populateShelleyTransEpoch() called during Load(). Several tests and mocks had import reorderings, struct field reorderings, and added accessors in test code.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Verify EpochFromSlot: correct era selection, epoch length/end handling, division-by-zero guards, and transition epoch use.
  • Ensure ChainSyncStatus.EpochNumber is set consistently on all update and rollback paths and tests reflect it.
  • Review transaction input resolution change: creation of txIndex@txId and use of kugo.Transaction(txID) for compatibility.
  • Inspect new config types and populate* helpers: defaults, YAML/env tags, and Load() invocation.
  • Review test/mocks changes and added MockBlockHeader methods (Era, Cbor) for correctness and test impacts:
    • input/chainsync/chainsync.go and chainsync_test.go
    • input/chainsync/block_test.go
    • internal/config/config.go
    • filter/chainsync/* and other updated test files (api/api_test.go, output/push/api_routes_test.go)

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes several out-of-scope changes: transaction input resolution refactoring, import reordering across multiple test files, test struct field reordering, and README documentation updates unrelated to the EpochNumber feature. Separate transaction input resolution changes and import/test reordering changes into distinct PRs focused on their respective objectives; keep license header restoration and EpochNumber addition together in this PR only.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly matches the main change: adding EpochNumber field to ChainSyncStatus, which is concise and clearly communicates the primary modification.
Linked Issues check ✅ Passed The PR successfully implements the requirement from issue #187: adds EpochNumber field to ChainSyncStatus via new EpochFromSlot() function and updates ChainSyncStatus struct with proper epoch population logic.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/license-header-restore-187

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/config/config.go (1)

17-25: Fix import grouping to satisfy gci

golangci-lint is failing with “File is not properly formatted (gci)”. Please regroup the imports (stdlib / third-party / module-local) or run the project’s gci formatter so the lint job passes.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f4fecb1 and 35b9af3.

📒 Files selected for processing (3)
  • input/chainsync/chainsync.go (3 hunks)
  • input/chainsync/chainsync_test.go (2 hunks)
  • internal/config/config.go (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
input/chainsync/chainsync.go (1)
internal/config/config.go (1)
  • GetConfig (201-203)
🪛 GitHub Actions: golangci-lint
internal/config/config.go

[error] 33-33: golangci-lint: File is not properly formatted (gci)

🪛 GitHub Check: lint
internal/config/config.go

[failure] 84-84:
error-format: fmt.Errorf can be replaced with errors.New (perfsprint)


[failure] 72-72:
error-format: fmt.Errorf can be replaced with errors.New (perfsprint)


[failure] 69-69:
error-format: fmt.Errorf can be replaced with errors.New (perfsprint)


[failure] 66-66:
error-format: fmt.Errorf can be replaced with errors.New (perfsprint)


[failure] 33-33:
File is not properly formatted (gci)

input/chainsync/chainsync.go

[failure] 44-44:
func getNetwork is unused (unused)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (go)

@wolf31o2 wolf31o2 force-pushed the chore/license-header-restore-187 branch from 35b9af3 to 373d1ff Compare November 7, 2025 04:21
@wolf31o2
Copy link
Member Author

wolf31o2 commented Nov 7, 2025

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Nov 7, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@wolf31o2
Copy link
Member Author

wolf31o2 commented Nov 7, 2025

Trying again...

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Nov 7, 2025

@wolf31o2: I'll trigger a review of the changes in this PR right away.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
internal/config/config.go (1)

51-76: Address redundant validation and zero-override issue.

Two problems:

  1. Redundant validation (lines 66-74): After setting defaults when values are zero (lines 54-65), the validation checks on lines 66-74 will always pass. This validation is dead code.

  2. Zero-override issue (unresolved from past review): The function overwrites zero values with mainnet defaults, preventing legitimate configurations for Byron-less networks that require EndSlot=0 and Epochs=0. This causes EpochFromSlot to incorrectly compute epochs on such networks.

As suggested in the past review, change EndSlot and Epochs to pointer types (*uint64) in ByronGenesisConfig so you can distinguish "unset" (nil) from explicit zero:

 type ByronGenesisConfig struct {
 	EpochLength        uint64  `yaml:"epoch_length"`
 	ByronSlotsPerEpoch uint64  `yaml:"byron_slots_per_epoch"`
-	EndSlot            uint64  `yaml:"end_slot"`
-	Epochs             uint64  `yaml:"epochs"`
+	EndSlot            *uint64 `yaml:"end_slot"`
+	Epochs             *uint64 `yaml:"epochs"`
 }

Then update populateByronGenesis to apply defaults only when nil:

 func (c *Config) populateByronGenesis() error {
 	cfg := &c.ByronGenesis
 	if cfg.EpochLength == 0 {
 		cfg.EpochLength = 21600
 	}
 	if cfg.ByronSlotsPerEpoch == 0 {
 		cfg.ByronSlotsPerEpoch = cfg.EpochLength
 	}
-	if cfg.EndSlot == 0 {
-		cfg.EndSlot = 4492799
+	if cfg.EndSlot == nil {
+		defaultEndSlot := uint64(4492799)
+		cfg.EndSlot = &defaultEndSlot
 	}
-	if cfg.Epochs == 0 {
-		cfg.Epochs = 208
+	if cfg.Epochs == nil {
+		defaultEpochs := uint64(208)
+		cfg.Epochs = &defaultEpochs
 	}
 	if cfg.EpochLength == 0 {
 		return errors.New("ByronGenesisConfig: EpochLength must be nonzero")
 	}
-	if cfg.EndSlot == 0 {
+	if cfg.EndSlot == nil || *cfg.EndSlot == 0 {
 		return errors.New("ByronGenesisConfig: EndSlot must be nonzero")
 	}
-	if cfg.Epochs == 0 {
+	if cfg.Epochs == nil || *cfg.Epochs == 0 {
 		return errors.New("ByronGenesisConfig: Epochs must be nonzero")
 	}
 	return nil
 }

Update globalConfig initialization and EpochFromSlot to use pointer dereference where needed.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 35b9af3 and 373d1ff.

📒 Files selected for processing (3)
  • input/chainsync/chainsync.go (3 hunks)
  • input/chainsync/chainsync_test.go (2 hunks)
  • internal/config/config.go (4 hunks)
🔇 Additional comments (3)
input/chainsync/chainsync.go (2)

107-107: LGTM: EpochNumber field addition.

The addition of EpochNumber to ChainSyncStatus aligns with the PR objective to enable downstream consumers to process per-epoch data more easily.


546-546: Verify error handling if EpochFromSlot signature changes.

The EpochNumber is correctly populated by calling EpochFromSlot(slotNumber). If you adopt the error-returning signature suggested in the earlier review comment, update this line to handle the error appropriately.

input/chainsync/chainsync_test.go (1)

1-13: LGTM: License header addition.

The Apache 2.0 license header is appropriate and consistent with the PR objective to restore license headers.

Comment on lines 43 to 69
// EpochFromSlot returns the epoch number for a given slot, using Cardano era rules.
func EpochFromSlot(slot uint64) uint64 {
cfg := config.GetConfig()
byron := cfg.ByronGenesis
shelley := cfg.ShelleyGenesis

if slot <= byron.EndSlot {
if byron.EpochLength == 0 {
return 0 // avoid div by zero
}
return slot / byron.EpochLength
}
shelleyStartEpoch := byron.Epochs
shelleyStartSlot := byron.EndSlot + 1
if shelley.EpochLength == 0 {
return shelleyStartEpoch // avoid div by zero
}
return shelleyStartEpoch + (slot-shelleyStartSlot)/shelley.EpochLength
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Document zero-epoch-length assumptions and consider error handling.

The division-by-zero guards (lines 50-51, 57-58) return hardcoded values that may produce incorrect epoch numbers in edge cases:

  • Line 51 returns 0 when byron.EpochLength == 0, even for slots that should be in the Shelley era.
  • Line 58 returns shelleyStartEpoch when shelley.EpochLength == 0, regardless of how far into the future the slot is.

These assumptions work if the configuration is always valid after populateByronGenesis() and populateShelleyGenesis() enforce non-zero epoch lengths. However, if those validations are bypassed or configurations allow zero values (e.g., for Byron-less networks), this function silently produces incorrect results.

Recommendations:

  1. Add validation in config population to ensure epoch lengths are always non-zero when these eras exist (addresses the config.go review comments).
  2. Document the assumptions in the function comment that epoch lengths must be non-zero.
  3. Consider returning an error instead of a best-guess value when encountering invalid configuration:
-func EpochFromSlot(slot uint64) uint64 {
+func EpochFromSlot(slot uint64) (uint64, error) {
 	cfg := config.GetConfig()
 	byron := cfg.ByronGenesis
 	shelley := cfg.ShelleyGenesis
 
 	if slot <= byron.EndSlot {
 		if byron.EpochLength == 0 {
-			return 0 // avoid div by zero
+			return 0, errors.New("Byron EpochLength is zero")
 		}
 		return slot / byron.EpochLength
 	}
 	shelleyStartEpoch := byron.Epochs
 	shelleyStartSlot := byron.EndSlot + 1
 	if shelley.EpochLength == 0 {
-		return shelleyStartEpoch // avoid div by zero
+		return 0, errors.New("Shelley EpochLength is zero")
 	}
-	return shelleyStartEpoch + (slot-shelleyStartSlot)/shelley.EpochLength
+	return shelleyStartEpoch + (slot-shelleyStartSlot)/shelley.EpochLength, nil
 }

Then update the call site on line 546 to handle the error.

Committable suggestion skipped: line range outside the PR's diff.

agaffney
agaffney previously approved these changes Nov 7, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
input/chainsync/chainsync_test.go (1)

81-81: Epoch assertion and comment are correct.

Good fix: Byron-era math is accurate and the EpochNumber=0 assertion matches 12345/21600. BlockNumber=0 on rollback is also clear.

Optionally, add a boundary check to avoid future off‑by‑one regressions:

t.Run("epoch boundary at Byron slot 21600", func(t *testing.T) {
	c := &ChainSync{status: &ChainSyncStatus{}, eventChan: make(chan event.Event, 1)}
	point := ocommon.Point{Slot: 21600}
	tip := chainsync.Tip{Point: ocommon.Point{Slot: 21600}}
	require.NoError(t, c.handleRollBackward(chainsync.CallbackContext{}, point, tip))
	assert.Equal(t, uint64(21600), c.status.SlotNumber)
	assert.Equal(t, uint64(1), c.status.EpochNumber) // slots 0..21599 => epoch 0; slot 21600 => epoch 1
})

Also applies to: 85-86

internal/config/config.go (4)

28-34: Good: pointers enable explicit zero for Byron-less networks. Consider env var tags for nested fields.

Switching EndSlot/Epochs to pointers unblocks zero-valued configs. To make env variables ergonomic/consistent for these nested fields, add explicit env tags (and/or split_words) so operators can set them via env without surprising names.

 type ByronGenesisConfig struct {
-    EpochLength        uint64  `yaml:"epoch_length"`
-    ByronSlotsPerEpoch uint64  `yaml:"byron_slots_per_epoch"`
-    EndSlot            *uint64 `yaml:"end_slot"`
-    Epochs             *uint64 `yaml:"epochs"`
+    EpochLength        uint64  `yaml:"epoch_length"        envconfig:"EPOCH_LENGTH"        split_words:"true"`
+    ByronSlotsPerEpoch uint64  `yaml:"byron_slots_per_epoch" envconfig:"SLOTS_PER_EPOCH"   split_words:"true"`
+    EndSlot            *uint64 `yaml:"end_slot"            envconfig:"END_SLOT"`
+    Epochs             *uint64 `yaml:"epochs"              envconfig:"EPOCHS"`
 }

And for Shelley:

 type ShelleyGenesisConfig struct {
-    EpochLength uint64 `yaml:"epoch_length"`
+    EpochLength uint64 `yaml:"epoch_length" envconfig:"EPOCH_LENGTH" split_words:"true"`
 }

47-74: Guard against divergence between EpochLength and ByronSlotsPerEpoch.

If both are provided and differ, behavior becomes ambiguous. Add a fast-fail (or normalize) to keep them consistent.

 func (c *Config) populateByronGenesis() error {
     cfg := &c.ByronGenesis
     if cfg.EpochLength == 0 {
         cfg.EpochLength = 21600
     }
-    if cfg.ByronSlotsPerEpoch == 0 {
-        cfg.ByronSlotsPerEpoch = cfg.EpochLength
-    }
+    if cfg.ByronSlotsPerEpoch == 0 {
+        cfg.ByronSlotsPerEpoch = cfg.EpochLength
+    } else if cfg.EpochLength != 0 && cfg.ByronSlotsPerEpoch != cfg.EpochLength {
+        return errors.New("ByronGenesisConfig: ByronSlotsPerEpoch must equal EpochLength")
+    }
     if cfg.EndSlot == nil {
         defaultEndSlot := uint64(4492799)
         cfg.EndSlot = &defaultEndSlot
     }

If you intend to deprecate one of these fields, consider marking one as canonical and documenting precedence. Based on learnings.


133-141: Set ByronSlotsPerEpoch explicitly in defaults to avoid pre-populate reads.

Some code/tests may access globalConfig before Load()/populate*. Being explicit prevents nil/zero surprises.

 ByronGenesis: ByronGenesisConfig{
     EpochLength: 21600,
+    ByronSlotsPerEpoch: 21600,
     EndSlot:     func() *uint64 { v := uint64(4492799); return &v }(),
     Epochs:      func() *uint64 { v := uint64(208); return &v }(),
 },

89-101: Tag nested struct fields for envconfig
Nested ByronGenesisConfig and ShelleyGenesisConfig fields (e.g. EpochLength, ByronSlotsPerEpoch) default to DUMMY_BYRONGENESIS_EPOCHLENGTH (no underscores) when processed by envconfig.Process("DUMMY", &c) (github.com). To generate DUMMY_BYRON_GENESIS_EPOCH_LENGTH (and similarly for other nested fields), add split_words:"true" or explicit envconfig:"BYRON_GENESIS_EPOCH_LENGTH" tags to each field.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 373d1ff and 0aa49be.

📒 Files selected for processing (3)
  • input/chainsync/chainsync.go (3 hunks)
  • input/chainsync/chainsync_test.go (2 hunks)
  • internal/config/config.go (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • input/chainsync/chainsync.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (go)
🔇 Additional comments (3)
internal/config/config.go (3)

76-83: Ack: removed redundant validation.

Simplified Shelley populate looks good and avoids dead checks.


160-165: Initialization order LGTM.

Populate Byron first (may set slots/epochs), then Shelley. Error wrapping is consistent.


28-34: ByronSlotsPerEpoch is used in populateByronGenesis – do not remove.

Likely an incorrect or invalid review comment.

@wolf31o2 wolf31o2 force-pushed the chore/license-header-restore-187 branch from 0aa49be to be7c575 Compare November 7, 2025 14:47
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
input/chainsync/chainsync.go (1)

43-69: Division-by-zero guards return potentially incorrect values.

The guards at lines 58-59 and 65-66 prevent panics but return hardcoded fallback values when epoch lengths are zero. As noted in past reviews, this can silently produce incorrect epoch numbers if the genesis configuration is invalid or missing validations.

Since past reviews have already flagged this issue and suggested returning an error instead of a best-guess value, please address that feedback by either:

  1. Ensuring validation in populateByronGenesis() and populateShelleyGenesis() makes zero epoch lengths impossible, or
  2. Returning an error from this function when invalid configuration is encountered
🧹 Nitpick comments (4)
filter/chainsync/chainsync_test.go (2)

95-98: Cosmetic field reordering in MockOutput.

The field order in MockOutput was changed (scriptRef moved before amount), but this is purely organizational and doesn't affect functionality. Go struct field order doesn't impact behavior, only memory layout.


209-219: Cosmetic field reordering in mock certificates.

The embedded struct fields were moved after cborData in both mockStakeDelegationCert and mockStakeDeregistrationCert. This is a cosmetic change that doesn't affect functionality.

input/chainsync/block_test.go (2)

24-33: Cosmetic field reordering in MockBlockHeader.

The fields in MockBlockHeader were reordered (era and cborBytes moved earlier), but this is purely organizational. The accessor methods remain unchanged, so test functionality is unaffected.


114-121: Field order changes in test cases are cosmetic.

The test case struct fields were reordered, but this doesn't affect test behavior. All fields are still properly initialized and assertions remain correct.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0aa49be and be7c575.

📒 Files selected for processing (5)
  • filter/chainsync/chainsync_test.go (3 hunks)
  • input/chainsync/block_test.go (3 hunks)
  • input/chainsync/chainsync.go (3 hunks)
  • input/chainsync/chainsync_test.go (2 hunks)
  • internal/config/config.go (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
input/chainsync/chainsync.go (1)
internal/config/config.go (1)
  • GetConfig (198-200)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (go)
🔇 Additional comments (6)
input/chainsync/chainsync.go (2)

115-115: LGTM: EpochNumber field added to ChainSyncStatus.

The addition of the EpochNumber field aligns with the PR objective and issue #187, enabling downstream consumers to track epochs. The field is properly populated in updateStatus() at line 554.


554-554: Note: Error handling may be needed if EpochFromSlot is updated.

The call to EpochFromSlot(slotNumber) populates EpochNumber correctly. However, if EpochFromSlot is updated to return an error (as suggested in past reviews), this call site will need to handle that error appropriately.

input/chainsync/chainsync_test.go (1)

89-90: LGTM: EpochNumber verification added to rollback test.

The assertion correctly verifies that EpochNumber is 0 after a rollback to slot 12345 in the Byron era (12345 / 21600 = 0). The comment accurately reflects the Byron era calculation, addressing the past review feedback.

internal/config/config.go (3)

28-45: LGTM: Genesis config types well-structured.

The new genesis configuration types are clearly defined with appropriate YAML tags. Using pointer types for EndSlot and Epochs (lines 30-31) correctly addresses past review feedback about distinguishing explicit zero values from unset fields.


80-86: LGTM: Shelley genesis population is clean.

Unlike the Byron equivalent, populateShelleyGenesis() correctly sets defaults without redundant validation afterwards.


164-168: LGTM: Genesis config population integrated correctly.

The Load() method properly calls both genesis population functions after processing environment variables, ensuring configs are validated and defaults are applied before use.

@wolf31o2 wolf31o2 force-pushed the chore/license-header-restore-187 branch 2 times, most recently from 1f0cc86 to 32d859b Compare November 7, 2025 16:32
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
input/chainsync/chainsync.go (1)

42-74: Consider error handling for invalid genesis configuration

The EpochFromSlot function returns fallback values when epoch lengths are zero (lines 63-64, 70-71), which may produce incorrect epoch numbers if the genesis configuration is invalid. A previous review suggested returning an error instead of a best-guess value.

However, the current implementation with fallback values is pragmatic if:

  1. The config population methods (populateByronGenesis() and populateShelleyGenesis()) always ensure non-zero epoch lengths
  2. Zero-length epochs are an acceptable edge case for testing or non-standard networks

Recommendation: Document the assumptions in the function comment that epoch lengths must be non-zero in normal operation, and that the fallback values are defensive programming for edge cases.

Add documentation:

-// EpochFromSlot derives an epoch from a slot using Byron/Shelley genesis params.
-// Byron slots: 0..EndSlot inclusive. Explicit zero EndSlot/Epochs means no Byron era.
-// Zero epoch length in either era yields a safe fallback (0 Byron / starting Shelley epoch).
+// EpochFromSlot derives an epoch from a slot using Byron/Shelley genesis params.
+// Byron slots: 0..EndSlot inclusive. Explicit zero EndSlot/Epochs means no Byron era.
+// Zero epoch length in either era yields a safe fallback (0 Byron / starting Shelley epoch).
+// 
+// Assumptions:
+// - populateByronGenesis() and populateShelleyGenesis() ensure non-zero epoch lengths in normal configs
+// - Zero-length fallbacks are defensive programming for edge cases and testing
+// - For production use, genesis config should always have valid epoch lengths
 func EpochFromSlot(slot uint64) uint64 {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f0cc86 and 32d859b.

📒 Files selected for processing (8)
  • api/api_test.go (1 hunks)
  • filter/chainsync/chainsync_test.go (4 hunks)
  • filter/chainsync/plugin_test.go (1 hunks)
  • input/chainsync/block_test.go (4 hunks)
  • input/chainsync/chainsync.go (4 hunks)
  • input/chainsync/chainsync_test.go (3 hunks)
  • internal/config/config.go (3 hunks)
  • output/push/api_routes_test.go (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • api/api_test.go
  • output/push/api_routes_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • input/chainsync/chainsync_test.go
  • filter/chainsync/chainsync_test.go
🧰 Additional context used
🧬 Code graph analysis (1)
input/chainsync/chainsync.go (1)
internal/config/config.go (1)
  • GetConfig (177-179)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (go)
🔇 Additional comments (11)
filter/chainsync/plugin_test.go (1)

7-8: LGTM: Import reordering

The import reordering is a formatting change with no functional impact.

input/chainsync/block_test.go (3)

9-10: LGTM: Import reordering

Import reorganization with no functional impact.


26-33: LGTM: MockBlockHeader field reordering

The field reordering aligns the mock struct with the interface methods (Era(), Cbor()) and improves readability by grouping related fields.


118-121: LGTM: Test case structure updated for networkMagic

The addition of the networkMagic field aligns with the event.NewBlockContext signature changes, ensuring the test properly validates the network context.

input/chainsync/chainsync.go (2)

559-559: LGTM: EpochNumber populated correctly

The EpochNumber field is properly populated using the EpochFromSlot function, which implements the epoch derivation logic using Byron/Shelley genesis parameters. This addresses the PR objective of adding epoch number to ChainSyncStatus.


120-120: LGTM: EpochNumber field added

The new EpochNumber field in ChainSyncStatus directly addresses issue #187 by exposing the epoch number to downstream consumers for per-epoch data processing.

internal/config/config.go (5)

27-38: LGTM: Genesis configuration structures

The new ByronGenesisConfig and ShelleyGenesisConfig structs properly support Byron-less networks by using pointer types for EndSlot and Epochs, allowing explicit zero values to be distinguished from unset values.


41-59: LGTM: Byron genesis population addresses past review concerns

The populateByronGenesis() method correctly:

  1. Uses pointer types to distinguish unset (nil) from explicit zero values
  2. Only applies defaults when values are nil, not when they're zero
  3. Removed redundant validation checks after setting defaults

This implementation allows Byron-less networks to set EndSlot=0 and Epochs=0 without being overwritten by mainnet defaults.


61-67: LGTM: Shelley genesis population simplified

The populateShelleyGenesis() method correctly applies defaults and removes the redundant validation that was flagged in the previous review. The implementation is clean and straightforward.


118-125: LGTM: Genesis defaults initialized correctly

The globalConfig initialization properly sets up mainnet defaults for both Byron and Shelley genesis configurations. The inline function syntax for pointer initialization (lines 120-121) is correct and idiomatic.


145-147: LGTM: Genesis population integrated into config load

The Load() method correctly calls both genesis population methods after environment processing, ensuring that defaults are applied consistently regardless of how the configuration is loaded.

@wolf31o2 wolf31o2 force-pushed the chore/license-header-restore-187 branch 2 times, most recently from 38fb3c9 to ea47f04 Compare November 7, 2025 17:01
@wolf31o2 wolf31o2 requested review from a team as code owners November 7, 2025 17:01
@wolf31o2 wolf31o2 force-pushed the chore/license-header-restore-187 branch from ea47f04 to 5a3c7c6 Compare November 7, 2025 17:19
@wolf31o2
Copy link
Member Author

wolf31o2 commented Nov 7, 2025

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Nov 7, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
input/chainsync/chainsync.go (1)

42-75: Document zero-epoch-length behavior or consider validation.

The division-by-zero guards (lines 64-66, 71-73) return fallback values that may be incorrect:

  • Byron era: returns 0 for all slots when EpochLength == 0, even for slots > 0
  • Shelley era: returns shelleyStartEpoch for all slots when EpochLength == 0, regardless of actual slot distance

While the config defaults ensure non-zero epoch lengths, explicit zero configurations (e.g., misconfigured Byron-less networks) would silently produce incorrect epoch numbers.

Recommendation: Add a brief doc comment explaining that this function assumes epoch lengths are non-zero (ensured by config population) and that zero-length configurations will return fallback values rather than errors.

Consider adding to the function comment:

// EpochFromSlot derives an epoch from a slot using Byron/Shelley genesis params.
// Assumes epoch lengths are non-zero (enforced by config defaults).
// Byron slots: 0..EndSlot inclusive. Explicit zero EndSlot/ShelleyTransEpoch means no Byron era.
// Zero epoch length in either era yields a safe fallback (0 Byron / starting Shelley epoch).
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea47f04 and 5a3c7c6.

📒 Files selected for processing (9)
  • README.md (1 hunks)
  • api/api_test.go (1 hunks)
  • filter/chainsync/chainsync_test.go (4 hunks)
  • filter/chainsync/plugin_test.go (1 hunks)
  • input/chainsync/block_test.go (4 hunks)
  • input/chainsync/chainsync.go (4 hunks)
  • input/chainsync/chainsync_test.go (5 hunks)
  • internal/config/config.go (3 hunks)
  • output/push/api_routes_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • filter/chainsync/plugin_test.go
  • input/chainsync/chainsync_test.go
  • api/api_test.go
  • output/push/api_routes_test.go
  • README.md
🔇 Additional comments (5)
filter/chainsync/chainsync_test.go (1)

29-30: LGTM: Clean test code reorganization.

The import reordering and struct field reordering improve code organization without affecting test logic or behavior.

Also applies to: 96-99, 212-213, 218-219, 257-258

input/chainsync/block_test.go (1)

9-10: LGTM: Mock enhancements support epoch functionality.

The additions of Era() and Cbor() methods to MockBlockHeader properly support the new epoch-related functionality introduced in this PR. The struct and test case field reordering improves organization.

Also applies to: 26-66, 118-121, 234-234

internal/config/config.go (1)

27-68: LGTM: Genesis configuration properly implemented.

The genesis configuration types and population logic correctly handle defaults while allowing Byron-less network configurations. The use of *uint64 for EndSlot properly distinguishes "unset" from explicit zero values, and the redundant validations flagged in previous reviews have been removed.

Also applies to: 76-87, 121-127, 148-150

input/chainsync/chainsync.go (2)

121-121: LGTM: EpochNumber properly integrated into chain sync status.

The EpochNumber field has been correctly added to ChainSyncStatus and is populated using EpochFromSlot(slotNumber) during status updates. This enables downstream consumers to process per-epoch data as intended by issue #187.

Also applies to: 560-560


660-662: LGTM: Transaction identifier format corrected.

The transaction identifier now uses the correct format (%d@%s with index before txId) as specified by the kugo library, addressing the issue raised in the previous review.

@wolf31o2 wolf31o2 requested a review from agaffney November 7, 2025 18:39
@wolf31o2 wolf31o2 merged commit 08535c7 into main Nov 8, 2025
11 checks passed
@wolf31o2 wolf31o2 deleted the chore/license-header-restore-187 branch November 8, 2025 00:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Epoch number to ChainSyncStatus

3 participants